
#ifndef __WEBSERVER__
#define __WEBSERVER__

#include <iostream>
#include "TcpSocket.hpp"
#include <unistd.h>
#include <thread>
#include <signal.h>
#include <functional>
#include "Entrance.hpp"
#include "ThreadPool.hpp"
#include <sys/socket.h>
#include "MyEpoll.hpp"

typedef std::function<void *(const int)> function;

#define QUEUE_TASK_NUM 3      // 任务队列最大数
#define EPOLL_EVENT_MAXNUM 20 // epoll就绪队列单次最大量

// 制作一个初始化服务端的类型——使用单例模式

template <class T>
class webserver
{
    typedef ThreadPool<T *> thread_pool;

private:
    int _port = -3;
    int _server_listen_socket = -3;
    thread_pool *st = nullptr;

    int _efd;
    struct epoll_event *_epoll_event_array;
    size_t _num;

public:
    webserver()
    {
        signal(SIGPIPE, SIG_IGN);
        // 防止send，向关闭管道写入，导致webserver崩溃
    }

    ~webserver()
    {
        if (_server_listen_socket > 0)
            close(_server_listen_socket);
        if (_efd > 0)
            close(_efd);
        if (_epoll_event_array != nullptr)
            delete[] _epoll_event_array;
    }

    void initial(int _port, thread_pool *th_pool)
    {
        _epoll_event_array = new epoll_event[EPOLL_EVENT_MAXNUM];
        if (_epoll_event_array == nullptr)
        {
            Logmessage(ERROR, "event array new fail");
            exit(3);
        }    

        _num = EPOLL_EVENT_MAXNUM;

        MySocket tool;
        _server_listen_socket = tool._socket_set();
        // 设置复用端口号
        int tmp = 1;
        if (setsockopt(_server_listen_socket, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) < 0)
            std::cout << "setsockopt fail" << std::endl;

        tool.bind_set(_server_listen_socket, _port);
        Logmessage(INFO, "服务端初始化成功");

        // 启用线程池
        st = th_pool;
        _efd = MyEpoll::CreateEpoll();
    }

    void LoopOnce(MySocket& tool)
    {
        // std::cerr << "_efd:" << _efd << std::endl;
        // std::cerr << "_num:" << _num << std::endl;
        // std::cerr << _epoll_event_array << std::endl;
        int n = MyEpoll::WaitEpoll(_efd, _epoll_event_array, _num, 1000);
        switch (n)
        {
        case -1:
            Logmessage(ERROR, "epoll wait fail");
            break;
        case 0:
            Logmessage(INFO, "timeout");
            break;
        default:
            for (int i = 0; i < n; i++)
            {
                int fd = _epoll_event_array[i].data.fd;
                uint32_t revent = _epoll_event_array[i].events;
                if (revent & EPOLLIN)
                {
                    // 1.listen, 普通socket加入到epoll红黑树中
                    if (fd == _server_listen_socket)
                    {
                        sockaddr_in cline;
                        uint16_t cline_prot;
                        int actual_socket = tool.accept_run(_server_listen_socket, &cline, &cline_prot);
                        if (actual_socket != -1)
                        {
                            Logmessage(INFO, "connect success 标识符:%d", actual_socket);
                        }

                        // 将普通socket加入到
                        MyEpoll::CtlEpoll(_efd, actual_socket, EPOLL_CTL_ADD, EPOLLIN);
                    }
                    // 2.accept socket 有读事件就绪，载入线程池进行处理
                    else
                    {
                        // 接收到请求后，想任务池中添加任务---线程池方案
                        if (st->Get_queue_task_reserver_size() < QUEUE_TASK_NUM)
                        {
                            Thread_Task *task = new Thread_Task(fd);
                            st->push(task);
                            st->swap_queue(QUEUE_TASK_NUM);
                        }
                        // 删除该普通socket的epoll关心
                        MyEpoll::CtlEpoll(_efd, fd, EPOLL_CTL_DEL, EPOLLIN);
                    }
                }
            }
            break;
        }
    }

    void work_begin()
    {
        MySocket tool;
        tool.listen_run(_server_listen_socket);
        MyEpoll::CtlEpoll(_efd, _server_listen_socket, EPOLL_CTL_ADD, EPOLLIN);
        while (1)
        {
            LoopOnce(tool);
        }
    }
};

#endif
